/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file rw_t4t.c
 * @brief T4T tag implementation
 */

#include "nci_feature.h"
#if IS_FEATURE_ON(NCI_FEATURE_RW_T4T)

#include "rw_tag.h"
#include "nfc_log_api.h"
#include "rw_int.h"
#include "discover_int.h"
#include "t4t_api.h"

#define T4T_ADDI_FRAME_RESP 0xAFu
#define T4T_DESELECT_REQ 0xC2

#define T4T_CC_FILE_ID 0xE103
#define T4T_LE_CC_FILE_MIN_LEN 0x0F
#define T4T_LE_CC_FILE_MAX_LEN 0x11

#define T4T_NDEF_FILE_NLEN_SIZE 2
#define T4T_NDEF_FILE_ENLEN_SIZE 4

#define T4T_TLV_NDEF_FILE_CONTROL 0x04
#define T4T_TLV_PROP_FILE_CONTROL 0x05
#define T4T_TLV_ENDEF_FILE_CONTROL 0x06
#define T4T_TLV_EPROP_FILE_CONTROL 0x07

#define T4T_TLV_LEN_NDEF_FILE_CONTROL 0x06
#define T4T_TLV_LEN_PROP_FILE_CONTROL 0x06
#define T4T_TLV_LEN_ENDEF_FILE_CONTROL 0x08
#define T4T_TLV_LEN_EPROP_FILE_CONTROL 0x08

#define T4T_FC_READ_ACCESS 0x00
#define T4T_FC_NO_READ_ACCESS 0xFF
#define T4T_FC_READ_ACCESS_PROP_START 0x80
#define T4T_FC_WRITE_ACCESS 0x00
#define T4T_FC_NO_WRITE_ACCESS 0xFF
#define T4T_FC_WRITE_ACCESS_PROP_START 0x80

#define T4T_FC_TLV_OFFSET_IN_CC 7
#define T4T_FC_WRITE_ACCESS_OFFSET_IN_TLV 7

#define T4T_DES_GET_VERSION_RSP_LEN 9

#define T4T_DESEV0_MAJOR_VERSION 0x00u
#define T4T_DESEV0_MINOR_VERSION 0x06u
#define T4T_DESEV1_MAJOR_VERSION 0x01u

#define T4T_SIZE_2K 0x16u
#define T4T_SIZE_4K 0x18u
#define T4T_SIZE_8K 0x1Au

#define T4T_DES_EV0_NFC_APP_ID 0x10EEEE
#define T4T_DES_EV1_NFC_APP_ID 0x010000

#define T4T_MAX_PER_WRITE_SIZE 512
#define T4T_MAX_PER_READ_SIZE 512

#define SELECT_FILE_P2 (s_t4t_info.version == T4T_VERSION_1_0 ? T4T_P2_FIRST_OR_ONLY_00 : T4T_P2_FIRST_OR_ONLY_0C)

typedef struct {
    uint16_t cclen;
    uint8_t version;
    uint16_t max_le;
    uint16_t max_lc;
    uint16_t ndef_file_id;
    uint32_t ndef_file_size;
    uint8_t ndef_len_size;
    uint8_t ndef_read_access;
    uint8_t ndef_write_access;
} tT4T_CC_FILE;

typedef struct {
    bool is_desfire;
    uint8_t version;
    uint16_t card_size;
    bool is_desfire_ev1;
#if IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)
    tT4T_CC_FILE cc_file;
    uint32_t ndef_len;
    uint32_t ndef_max_size;
    bool is_ndef_detected;
    bool is_ndef_aid_selected;
#endif
} tT4T_INFO;

static tT4T_INFO s_t4t_info;
static tT4T_TARGET s_t4t_rw = {
    .transmit = rw_transmit,
};

#if IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)

const static uint8_t s_ndef_aid_v10[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00};
const static uint8_t s_ndef_aid_v20[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01};

extern bool g_dta_mode_enabled;

static tNCI_STATUS t4t_select_ndef_aid(tNCI_BUF **pp_rsp) {
    tT4T_INFO *p_t4t = &s_t4t_info;
    if (p_t4t->is_ndef_aid_selected) {
        return NCI_STATUS_OK;
    }
    t4t_reset(&s_t4t_rw);
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    if (p_t4t->version == T4T_VERSION_UNKNOWN) {
        NFCLOG_I("t4t version unknown, try NDEF AID 2.0 first");
        p_t4t->version = T4T_VERSION_3_0;
        ret = t4t_select_application(&s_t4t_rw, s_ndef_aid_v20, T4T_NDEF_AID_LEN, pp_rsp);
        if (ret == NCI_STATUS_EXT_APDU_STATUS_ERROR) {
            p_t4t->version = T4T_VERSION_1_0;
            ret = t4t_select_application(&s_t4t_rw, s_ndef_aid_v10, T4T_NDEF_AID_LEN, pp_rsp);
        }
    } else if (p_t4t->version == T4T_VERSION_2_0 || p_t4t->version == T4T_VERSION_3_0) {
        ret = t4t_select_application(&s_t4t_rw, s_ndef_aid_v20, T4T_NDEF_AID_LEN, pp_rsp);
    } else if (p_t4t->version == T4T_VERSION_1_0) {
        ret = t4t_select_application(&s_t4t_rw, s_ndef_aid_v10, T4T_NDEF_AID_LEN, pp_rsp);
    }
    if (ret == NCI_STATUS_OK) {
        p_t4t->is_ndef_aid_selected = true;
    }
    return ret;
}

static tNCI_STATUS t4t_send_desfire_apdu(uint8_t ins, uint16_t lc, const uint8_t *p_dat, tNCI_BUF **pp_rsp) {
    tT4T_APDU apdu = {
        .cla = T4T_CLA_DESFIRE,
        .ins = ins,
        .lc = lc,
        .p_dat = p_dat,
        .le_present = true,
        .le = 0,
        .skip_verify_sw = true,
    };
    return t4t_send_apdu(&s_t4t_rw, &apdu, pp_rsp);
}

static bool is_cc_file_valide() {
    tT4T_INFO *p_t4t = &s_t4t_info;
    tT4T_CC_FILE *p_cc = &p_t4t->cc_file;
    uint8_t cc_version = p_cc->version;
    uint8_t cc_major_version = cc_version >> 4;
    uint8_t t4t_major_version = p_t4t->version >> 4;
    if (cc_major_version > t4t_major_version) {
        NFCLOG_E("cc file version is higher than t4t, cc=%d t4t=%d", cc_major_version, t4t_major_version);
        return false;
    }
    if (p_cc->max_le < 0x0F) {
        NFCLOG_E("cc max le is too small, cc=%d", p_cc->max_le);
        return false;
    }
    if (p_cc->max_lc < 0x01 || (g_dta_mode_enabled && (p_cc->max_lc < 0x0D))) {
        NFCLOG_E("cc max lc is too small, cc=%d", p_cc->max_lc);
        return false;
    }
    uint16_t ndef_file_id = p_cc->ndef_file_id;
    if ((ndef_file_id == T4T_CC_FILE_ID) ||
        (ndef_file_id == 0xE102) ||
        (ndef_file_id == 0x0000 && (cc_version = 0x20 || cc_version == 0x30)) ||
        (ndef_file_id == 0x3F00) ||
        (ndef_file_id == 0x3FFF) ||
        (ndef_file_id == 0xFFFF)) {
        NFCLOG_E("cc ndef file id is invalid, cc=0x%04X", ndef_file_id);
        return false;
    }
    uint32_t ndef_file_size = p_cc->ndef_file_size;
    if ((cc_version == 0x20 && (ndef_file_size < 0x0005 || ndef_file_size > 0x7FFF)) ||
        (cc_version == 0x30 && (ndef_file_size < 0x00000007 || ndef_file_size == 0xFFFFFFFF))) {
        NFCLOG_E("cc ndef file size is invalid, cc=0x%08X", ndef_file_size);
        return false;
    }
    uint8_t read_access = p_cc->ndef_read_access;
    uint8_t write_access = p_cc->ndef_write_access;
    if ((read_access > T4T_FC_READ_ACCESS && read_access < T4T_FC_READ_ACCESS_PROP_START) ||
        (read_access == T4T_FC_NO_READ_ACCESS)) {
        NFCLOG_E("cc ndef read access is invalid, cc=0x%02X", read_access);
        return false;
    }
    if (write_access > T4T_FC_WRITE_ACCESS && write_access < T4T_FC_WRITE_ACCESS_PROP_START) {
        NFCLOG_E("cc ndef write access is invalid, cc=0x%02X", write_access);
        return false;
    }
    return true;
}

static tNCI_STATUS parse_cc_file(uint8_t *p_cc) {
    tT4T_INFO *p_t4t = &s_t4t_info;
    uint16_t cc_file_len = 0;
    STREAM_TO_UINT16_BE(cc_file_len, p_cc);
    STREAM_TO_UINT8(p_t4t->cc_file.version, p_cc);
    STREAM_TO_UINT16_BE(p_t4t->cc_file.max_le, p_cc);
    STREAM_TO_UINT16_BE(p_t4t->cc_file.max_lc, p_cc);
    if (p_t4t->cc_file.max_lc > T4T_MAX_PER_WRITE_SIZE) {
        p_t4t->cc_file.max_lc = T4T_MAX_PER_WRITE_SIZE;
    }
    if (p_t4t->cc_file.max_le > T4T_MAX_PER_READ_SIZE) {
        p_t4t->cc_file.max_le = T4T_MAX_PER_READ_SIZE;
    }
    uint8_t type = 0, len = 0;
    STREAM_TO_UINT8(type, p_cc);
    STREAM_TO_UINT8(len, p_cc);
    if (type == T4T_TLV_NDEF_FILE_CONTROL && len == T4T_TLV_LEN_NDEF_FILE_CONTROL) {
        STREAM_TO_UINT16_BE(p_t4t->cc_file.ndef_file_id, p_cc);
        STREAM_TO_UINT16_BE(p_t4t->cc_file.ndef_file_size, p_cc);
        STREAM_TO_UINT8(p_t4t->cc_file.ndef_read_access, p_cc);
        STREAM_TO_UINT8(p_t4t->cc_file.ndef_write_access, p_cc);
        p_t4t->cc_file.ndef_len_size = T4T_NDEF_FILE_NLEN_SIZE;
    } else if (type == T4T_TLV_ENDEF_FILE_CONTROL && len == T4T_TLV_LEN_ENDEF_FILE_CONTROL) {
        STREAM_TO_UINT16_BE(p_t4t->cc_file.ndef_file_id, p_cc);
        STREAM_TO_UINT32_BE(p_t4t->cc_file.ndef_file_size, p_cc);
        STREAM_TO_UINT8(p_t4t->cc_file.ndef_read_access, p_cc);
        STREAM_TO_UINT8(p_t4t->cc_file.ndef_write_access, p_cc);
        p_t4t->cc_file.ndef_len_size = T4T_NDEF_FILE_ENLEN_SIZE;
    } else {
        NFCLOG_E("parse cc file failed, type=%d len=%d", type, len);
        return NCI_STATUS_EXT_FAILED;
    }
    NFCLOG_I("cc file version=%d max_le=%d max_lc=%d ndef_file_id=0x%04X ndef_file_size=0x%08X ndef_read_access=0x%02X ndef_write_access=0x%02X",
        p_t4t->cc_file.version, p_t4t->cc_file.max_le, p_t4t->cc_file.max_lc, p_t4t->cc_file.ndef_file_id, p_t4t->cc_file.ndef_file_size,
        p_t4t->cc_file.ndef_read_access, p_t4t->cc_file.ndef_write_access);
    if (!is_cc_file_valide()) {
        NFCLOG_E("cc file is invalid");
        return NCI_STATUS_EXT_FAILED;
    }
    return NCI_STATUS_OK;
}

static tNCI_STATUS scan_t4t_data() {
    /**
     * T4T扫描流程
     * 1. 选择NDEF AID
     * 2. 选择CC文件
     * 3. 读取CC文件
     * 4. 选择NDEF文件
     * 5. 读取NDEF文件前2Byte（NLEN）
     */
    tNCI_BUF *rsp = NULL;
    tT4T_INFO *p_t4t = &s_t4t_info;
    if (p_t4t->is_ndef_detected) {
        return NCI_STATUS_OK;
    }
    // 先选择NDEF AID
    tNCI_STATUS ret = t4t_select_ndef_aid(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select application failed, ret=%d", ret);
        goto cleanup;
    }
    // 选择CC文件
    ret = t4t_select_file(&s_t4t_rw, T4T_CC_FILE_ID, SELECT_FILE_P2, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select cc file failed, ret=%d", ret);
        goto cleanup;
    }
    // 先按照标准CC文件长度读取
    ret = t4t_read_binary(&s_t4t_rw, 0, T4T_LE_CC_FILE_MIN_LEN, true, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t read cc file failed, ret=%d", ret);
        goto cleanup;
    }
    uint8_t cc_file_data[T4T_LE_CC_FILE_MAX_LEN] = {0};
    if (!osi_memcpy_s(cc_file_data, T4T_LE_CC_FILE_MAX_LEN, rsp->data, T4T_LE_CC_FILE_MIN_LEN)) {
        NFCLOG_E("copy cc file data failed");
        ret = NCI_STATUS_EXT_COPY_FAILED;
        goto cleanup;
    }
    if (cc_file_data[T4T_FC_TLV_OFFSET_IN_CC] == T4T_TLV_ENDEF_FILE_CONTROL &&
        cc_file_data[T4T_FC_TLV_OFFSET_IN_CC + 1] == T4T_TLV_LEN_ENDEF_FILE_CONTROL) {
        // 是扩展的TLV格式，还需要继续读剩余部分
        const uint8_t endef_value_start = 0x09; // 0x07 + type + len
        ret = t4t_read_binary(&s_t4t_rw, endef_value_start, T4T_TLV_LEN_ENDEF_FILE_CONTROL, true, &rsp);
        if (ret != NCI_STATUS_OK) {
            NFCLOG_E("t4t read cc file failed, ret=%d", ret);
            goto cleanup;
        }
        if (!osi_memcpy_s(cc_file_data + endef_value_start, T4T_LE_CC_FILE_MAX_LEN - endef_value_start, rsp->data, T4T_TLV_LEN_ENDEF_FILE_CONTROL)) {
            NFCLOG_E("copy cc file data failed");
            ret = NCI_STATUS_EXT_COPY_FAILED;
            goto cleanup;
        }
    }
    // 解析CC文件
    ret = parse_cc_file(cc_file_data);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("parse cc file failed, ret=%d", ret);
        goto cleanup;
    }
    // 选择NDEF文件
    ret = t4t_select_file(&s_t4t_rw, p_t4t->cc_file.ndef_file_id, SELECT_FILE_P2, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select ndef file failed, ret=%d", ret);
        goto cleanup;
    }
    // 读取NDEF文件的NLEN字段（NDEF文件的前2byte）
    ret = t4t_read_binary(&s_t4t_rw, 0, p_t4t->cc_file.ndef_len_size, true, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t read ndef file failed, ret=%d", ret);
        goto cleanup;
    }
    if (p_t4t->cc_file.ndef_len_size == 2) {
        p_t4t->ndef_len = (uint16_t)(rsp->data[0] << 8) | rsp->data[1];
    } else {
        p_t4t->ndef_len = (uint32_t)(rsp->data[0] << 24) | (rsp->data[1] << 16) |
                              (rsp->data[2] << 8) | rsp->data[3];
    }
    p_t4t->ndef_max_size = p_t4t->cc_file.ndef_file_size - p_t4t->cc_file.ndef_len_size;
    p_t4t->is_ndef_detected = true;
cleanup:
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS ndef_detect(tNCI_RW_NDEF_INFO *p_ndef_info) {
    tT4T_INFO *p_t4t = &s_t4t_info;
    p_t4t->is_ndef_detected = false;
    tNCI_STATUS ret = scan_t4t_data();
    if (ret != NCI_STATUS_OK) {
        return ret;
    }
    if (!p_t4t->is_ndef_detected) {
        return NCI_STATUS_EXT_NOT_SUPPORTED;
    }
    p_ndef_info->curr_size = p_t4t->ndef_len;
    p_ndef_info->max_size = p_t4t->ndef_max_size;
    if (p_t4t->cc_file.ndef_write_access == T4T_FC_WRITE_ACCESS) {
        p_ndef_info->mode = NCI_RW_NDEF_MODE_READ_WRITE;
    } else {
        p_ndef_info->mode = NCI_RW_NDEF_MODE_READ_ONLY;
    }
    return NCI_STATUS_OK;
}

static tNCI_STATUS ndef_read(tNCI_RSP *p_ndef) {
    /**
     * NDEF读取步骤
     * 0. 检查NDEF检测状态
     * 1. 选择NDEF文件
     * 2. 读取NDEF文件
     */
    tT4T_INFO *p_t4t = &s_t4t_info;
    if (!p_t4t->is_ndef_detected) {
        NFCLOG_E("NDEF not detected");
        return NCI_STATUS_EXT_FAILED;
    }
    uint32_t ndef_len = p_t4t->ndef_len;
    if (ndef_len == 0) {
        NFCLOG_E("empty ndef message");
        return NCI_STATUS_EXT_EMPTY_DATA;
    }
    tNCI_BUF *rsp = NULL;
    // 选择NDEF文件
    tNCI_STATUS ret = t4t_select_file(&s_t4t_rw, p_t4t->cc_file.ndef_file_id, SELECT_FILE_P2, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select ndef file failed, ret=%d", ret);
        goto cleanup;
    }
    // 读取NDEF文件
    uint16_t max_le = p_t4t->cc_file.max_le;
    p_ndef->data = osi_calloc(ndef_len);
    if (!p_ndef->data) {
        NFCLOG_E("alloc ndef data failed");
        ret = NCI_STATUS_EXT_ALLOC_FAILED;
        goto cleanup;
    }
    while (p_ndef->len < ndef_len) {
        // 每轮循环最多读取max_le字节
        uint16_t le = (ndef_len - p_ndef->len > max_le) ? max_le : ndef_len - p_ndef->len;
        uint16_t rsp_offset = 0;
        if (p_t4t->cc_file.version >= T4T_VERSION_3_0) {
            ret = t4t_read_binary_odo(&s_t4t_rw, p_ndef->len + p_t4t->cc_file.ndef_len_size, max_le, &rsp, &rsp_offset);
        } else  {
            ret = t4t_read_binary(&s_t4t_rw, p_ndef->len + p_t4t->cc_file.ndef_len_size, le, true, &rsp);
        }
        if (ret != NCI_STATUS_OK) {
            NFCLOG_E("t4t read binary failed, ret=%d", ret);
            goto cleanup;
        }
        uint16_t rsp_len = rsp->len - 2 - rsp_offset; // 减去sw的2字节+偏移量
        if (rsp_len > le) {
            rsp_len = le;
        }
        if (!osi_memcpy_s(p_ndef->data + p_ndef->len, ndef_len - p_ndef->len, rsp->data + rsp_offset, rsp_len)) {
            NFCLOG_E("copy ndef data failed");
            ret = NCI_STATUS_EXT_COPY_FAILED;
            goto cleanup;
        }
        p_ndef->len += rsp_len;
    }
    if (p_ndef->len != ndef_len) {
        NFCLOG_E("read ndef data len=%d, expected=%d", p_ndef->len, ndef_len);
        ret = NCI_STATUS_EXT_FAILED;
        goto cleanup;
    }
    ret = NCI_STATUS_OK;
cleanup:
    if (ret != NCI_STATUS_OK) {
        if (p_ndef->data) {
            osi_free(p_ndef->data);
        }
        p_ndef->data = NULL;
        p_ndef->len = 0;
    }
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS is_ndef_formatable() {
    tT4T_INFO *p_t4t = &s_t4t_info;
    tNCI_STATUS ret = NCI_STATUS_EXT_NOT_SUPPORTED;
    if (!p_t4t->is_desfire) {
        return ret;
    }
    tNCI_BUF *rsp = NULL;
    ret = t4t_send_desfire_apdu(T4T_INS_GET_HW_VERSION, 0, NULL, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t get desfire version failed, ret=%d", ret);
        goto cleanup;
    }
    if (rsp->len > T4T_SW_LEN && rsp->data[rsp->len - 2] == 0x91 && rsp->data[rsp->len - 1] == 0xAF) {
        ret = NCI_STATUS_OK;
    } else {
        ret = NCI_STATUS_EXT_NOT_SUPPORTED;
    }
cleanup:
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS t4t_desfire_get_version(tNCI_BUF **pp_rsp) {
    // 获取Desfire软件版本
    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_ADDI_FRAME_RESP, 0, NULL, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t get desfire sw version failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    if (rsp->data[rsp->len - 1] != T4T_ADDI_FRAME_RESP) {
        NFCLOG_E("t4t check sw failed, ret=%d", ret);
        return NCI_STATUS_EXT_FAILED;
    }
    uint8_t major_version = rsp->data[3];
    uint8_t minor_version = rsp->data[4];
    uint16_t card_size = 0;
    bool is_desfire_ev1 = false;
    if (major_version == T4T_DESEV0_MAJOR_VERSION && minor_version == T4T_DESEV0_MINOR_VERSION) {
        card_size = 0xEDE;
    } else if (major_version >= T4T_DESEV1_MAJOR_VERSION) {
        is_desfire_ev1 = true;
        switch (rsp->data[5]) {
            case T4T_SIZE_2K:
                card_size = 2048;
                break;
            case T4T_SIZE_4K:
                card_size = 4096;
                break;
            case T4T_SIZE_8K:
                card_size = 8192;
                break;
            default:
                NFCLOG_E("unknown card size");
                return NCI_STATUS_EXT_FAILED;
        }
    } else {
        NFCLOG_E("unknown desfire version, major_version=%d, minor_version=%d", major_version, minor_version);
        return NCI_STATUS_EXT_FAILED;
    }
    s_t4t_info.is_desfire_ev1 = is_desfire_ev1;
    s_t4t_info.card_size = card_size;
    return ret;
}

static tNCI_STATUS t4t_desfire_get_uid(tNCI_BUF **pp_rsp) {
    // 获取Desfire UID
    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_ADDI_FRAME_RESP, 0, NULL, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t get desfire uid failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    uint16_t sw = (uint16_t)(rsp->data[rsp->len - 2] << 8) | rsp->data[rsp->len - 1];
    if (sw != T4T_SW_DES_OK) {
        NFCLOG_E("t4t check uid sw error, sw=%04X", sw);
        return NCI_STATUS_EXT_FAILED;
    }
    return ret;
}

static tNCI_STATUS t4t_desfire_create_app(tNCI_BUF **pp_rsp) {
    // 创建NDEF应用
    uint8_t param_data[3 + 2 + 2 + sizeof(s_ndef_aid_v20)] = {0};
    uint8_t *p = param_data;
    uint16_t param_len = 0;
    if (s_t4t_info.is_desfire_ev1) {
        UINT24_TO_STREAM_BE(p, T4T_DES_EV1_NFC_APP_ID);
        UINT16_TO_STREAM_BE(p, 0x0F21); // Key settings and no.of keys
        UINT16_TO_STREAM_BE(p, 0x05E1); // ISO file ID
        ARRAY_TO_STREAM_BE(p, s_ndef_aid_v20, sizeof(s_ndef_aid_v20)); // DF file name
        param_len = 14;
    } else {
        UINT24_TO_STREAM_BE(p, T4T_DES_EV0_NFC_APP_ID);
        UINT16_TO_STREAM_BE(p, 0x0F01); // Key settings and no.of keys
        param_len = 5;
    }
    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_INS_CREATE_APP, param_len, param_data, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t create app failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    uint16_t sw = (uint16_t)(rsp->data[rsp->len - 2] << 8) | rsp->data[rsp->len - 1];
    if (sw == T4T_SW_DES_DUPLICATE_ERROR) {
        sw = T4T_SW_DES_OK;
    }
    if (sw != T4T_SW_DES_OK) {
        NFCLOG_E("t4t create app sw error, sw=%04X", sw);
        return NCI_STATUS_EXT_FAILED;
    }
    return ret;
}

static tNCI_STATUS t4t_desfire_select_app(tNCI_BUF **pp_rsp) {
    // 选择应用
    uint8_t param_data[3] = {0};
    uint8_t *p = param_data;
    uint16_t param_len = 0;
    if (s_t4t_info.is_desfire_ev1) {
        UINT24_TO_STREAM_BE(p, T4T_DES_EV1_NFC_APP_ID);
    } else {
        UINT24_TO_STREAM_BE(p, T4T_DES_EV0_NFC_APP_ID);
    }
    param_len = 3;
    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_INS_SELECT_APP, param_len, param_data, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select app failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    uint16_t sw = (uint16_t)(rsp->data[rsp->len - 2] << 8) | rsp->data[rsp->len - 1];
    if (sw != T4T_SW_DES_OK) {
        NFCLOG_E("t4t select app sw error, sw=%04X", sw);
        return NCI_STATUS_EXT_FAILED;
    }
    return ret;
}

static tNCI_STATUS t4t_desfire_create_cc_file(tNCI_BUF **pp_rsp) {
    // 创建CC File
    uint8_t param_data[9] = {0};
    uint8_t *p = param_data;
    uint16_t param_len = 0;
    if (s_t4t_info.is_desfire_ev1) {
        UINT8_TO_STREAM(p, 0x01); // EV1 CC file id
        UINT16_TO_STREAM_BE(p, 0x03E1); // ISO file id
        param_len = 3;
    } else {
        UINT8_TO_STREAM(p, 0x03); // DESFire CC file id
        param_len = 1;
    }
    UINT8_TO_STREAM(p, 0x00); // COMM settings
    UINT16_TO_STREAM_BE(p, 0xEEEE); // Access rights
    UINT24_TO_STREAM_BE(p, 0x0F0000); // Set file size
    param_len += 6;

    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_INS_CREATE_DATAFILE, param_len, param_data, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t create cc file failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    uint16_t sw = (uint16_t)(rsp->data[rsp->len - 2] << 8) | rsp->data[rsp->len - 1];
    if (sw == T4T_SW_DES_DUPLICATE_ERROR) {
        sw = T4T_SW_DES_OK;
    }
    if (sw != T4T_SW_DES_OK) {
        NFCLOG_E("t4t create cc file sw error, sw=%04X", sw);
        return NCI_STATUS_EXT_FAILED;
    }
    return ret;
}

static tNCI_STATUS t4t_desfire_create_ndef_file(tNCI_BUF **pp_rsp) {
    // 创建NDEF File
    uint8_t param_data[9] = {0};
    uint8_t *p = param_data;
    uint16_t param_len = 0;
    if (s_t4t_info.is_desfire_ev1) {
        UINT8_TO_STREAM(p, 0x02); // EV1 NDEF file id
        UINT16_TO_STREAM_BE(p, 0x04E1); // ISO file id
        param_len = 3;
    } else {
        UINT8_TO_STREAM(p, 0x04); // DESFire NDEF file id
        param_len = 1;
    }
    UINT8_TO_STREAM(p, 0x00); // COMM settings
    UINT16_TO_STREAM_BE(p, 0xEEEE); // Access rights
    UINT16_TO_STREAM_LE(p, s_t4t_info.card_size); // Set file size
    UINT8_TO_STREAM(p, 0x00); // Set card size
    param_len += 6;
    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_INS_CREATE_DATAFILE, param_len, param_data, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t create ndef file failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    uint16_t sw = (uint16_t)(rsp->data[rsp->len - 2] << 8) | rsp->data[rsp->len - 1];
    if (sw == T4T_SW_DES_DUPLICATE_ERROR) {
        sw = T4T_SW_DES_OK;
    }
    if (sw != T4T_SW_DES_OK) {
        NFCLOG_E("t4t create ndef file sw error, sw=%04X", sw);
        return NCI_STATUS_EXT_FAILED;
    }
    return ret;
}

static tNCI_STATUS t4t_desfire_write_cc_file(tNCI_BUF **pp_rsp) {
    // 写入CC File
    uint8_t cc_file_bytes[] = {0x00, 0x0F, 0x10, 0x00, 0x3B, 0x00, 0x34, 0x04, 0x06, 0xE1, 0x04, 0x04, 0x00, 0x00, 0x00};
    uint16_t param_len = sizeof(cc_file_bytes) + 7;
    uint8_t param_data[sizeof(cc_file_bytes) + 7] = {0};
    uint8_t *p = param_data;
    if (s_t4t_info.is_desfire_ev1) {
        cc_file_bytes[2] = 0x20;
        cc_file_bytes[11] = s_t4t_info.card_size >> 8;
        cc_file_bytes[12] = s_t4t_info.card_size & 0xFF;
        UINT8_TO_STREAM(p, 0x01); // EV1 CC file id
    } else {
        UINT8_TO_STREAM(p, 0x03); // DESFire CC file id
    }
    UINT24_TO_STREAM_BE(p, 0x000000); // Set the offset
    UINT24_TO_STREAM_BE(p, 0x0F0000); // Set available length
    ARRAY_TO_STREAM_BE(p, cc_file_bytes, sizeof(cc_file_bytes));
    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_INS_DES_WRITE, param_len, param_data, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t write cc file failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    uint16_t sw = (uint16_t)(rsp->data[rsp->len - 2] << 8) | rsp->data[rsp->len - 1];
    if (sw != T4T_SW_DES_OK) {
        NFCLOG_E("t4t write cc file sw error, sw=%04X", sw);
        return NCI_STATUS_EXT_FAILED;
    }
    return ret;
}

static tNCI_STATUS t4t_desfire_write_ndef_file(tNCI_BUF **pp_rsp) {
    // 写入NDEF File
    uint8_t param_data[9] = {0};
    uint16_t param_len = 9;
    uint8_t *p = param_data;
    if (s_t4t_info.is_desfire_ev1) {
        UINT8_TO_STREAM(p, 0x02); // EV1 NDEF file id
    } else {
        UINT8_TO_STREAM(p, 0x04); // DESFire NDEF file id
    }
    UINT24_TO_STREAM_BE(p, 0x000000); // Set the offset
    UINT24_TO_STREAM_BE(p, 0x020000); // Set available length
    UINT16_TO_STREAM_BE(p, 0x0000); // Set NLEN
    tNCI_STATUS ret = t4t_send_desfire_apdu(T4T_INS_DES_WRITE, param_len, param_data, pp_rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t write ndef file failed, ret=%d", ret);
        return ret;
    }
    tNCI_BUF *rsp = *pp_rsp;
    uint16_t sw = (uint16_t)(rsp->data[rsp->len - 2] << 8) | rsp->data[rsp->len - 1];
    if (sw != T4T_SW_DES_OK) {
        NFCLOG_E("t4t write ndef file sw error, sw=%04X", sw);
        return NCI_STATUS_EXT_FAILED;
    }
    return ret;
}


static tNCI_STATUS ndef_format() {
    /**
     * NDEF Format步骤
     * 1. 获取Desfire硬件版本
     * 2. 获取Desfire软件版本
     * 3. 获取Desfire UID
     * 4. 创建Desfire NDEF应用
     * 5. 选择Desfire NDEF应用
     * 6. 创建Desfire CC文件
     * 7. 创建Desfire NDEF文件
     * 8. 写入Desfire CC文件数据
     * 9. 写入Desfire NDEF文件数据
     */
    tNCI_BUF *rsp = NULL;
    tNCI_STATUS ret = is_ndef_formatable();
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("tag not support to format ndef, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_get_version(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t get desfire version failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_get_uid(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t get desfire uid failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_create_app(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t create desfire app failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_select_app(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select desfire app failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_create_cc_file(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t create desfire cc file failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_create_ndef_file(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t create desfire ndef file failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_write_cc_file(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t write desfire cc file failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_desfire_write_ndef_file(&rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t write desfire ndef file failed, ret=%d", ret);
        goto cleanup;
    }

    NFCLOG_I("NDEF format success");
cleanup:
    s_t4t_info.is_ndef_detected = false;
    s_t4t_info.version = T4T_VERSION_UNKNOWN;
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS ndef_write(uint8_t *p_data, uint32_t data_len) {
    /**
     * NDEF Write步骤
     * 0. 检查NDEF检测状态
     * 1. 判断NDEF文件是否可写
     * 2. 判断NDEF文件大小是否满足写入数据
     * 3. 选择NDEF文件
     * 4. 更新NDEF FILE的NLEN字段为0x00, 0x00
     * 5. 写入NDEF文件
     * 6. 更新NDEF FILE的NLEN字段为实际写入数据长度
     */
    tT4T_INFO *p_t4t = &s_t4t_info;
    if (!p_t4t->is_ndef_detected) {
        NFCLOG_E("NDEF is not detected");
        return NCI_STATUS_EXT_FAILED;
    }
    if (p_t4t->cc_file.ndef_write_access != T4T_FC_WRITE_ACCESS) {
        NFCLOG_E("NDEF file is not writable, access=%d", p_t4t->cc_file.ndef_write_access);
        return NCI_STATUS_EXT_FAILED;
    }
    if (data_len > p_t4t->ndef_max_size) {
        NFCLOG_E("Cannot write NDEF of size greather than %d bytes, NDEF data length=%d", p_t4t->ndef_max_size, data_len);
        return NCI_STATUS_EXT_FAILED;
    }
    tNCI_BUF *rsp = NULL;
    tNCI_STATUS ret = t4t_select_file(&s_t4t_rw, p_t4t->cc_file.ndef_file_id, SELECT_FILE_P2, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select ndef file failed, ret=%d", ret);
        goto cleanup;
    }
    // 先更新NDEF FILE的NLEN字段为0x00, 0x00
    uint8_t ndef_len[T4T_NDEF_FILE_ENLEN_SIZE] = {0x00, 0x00, 0x00, 0x00};
    ret = t4t_write_binary(&s_t4t_rw, 0, p_t4t->cc_file.ndef_len_size, ndef_len, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t write ndef file failed, ret=%d", ret);
        goto cleanup;
    }
    p_t4t->ndef_len = 0;
    uint16_t max_lc = p_t4t->cc_file.max_lc;
    uint32_t write_len = 0;

    while (write_len < data_len) {
        uint16_t write_size = (data_len - write_len > max_lc) ? max_lc : data_len - write_len;
        if (p_t4t->cc_file.version >= T4T_VERSION_3_0) {
            ret = t4t_write_binary_odo(&s_t4t_rw, write_len + p_t4t->cc_file.ndef_len_size, &p_data[write_len], p_t4t->cc_file.max_lc, &rsp, &write_size);
        } else {
            ret = t4t_write_binary(&s_t4t_rw, write_len + p_t4t->cc_file.ndef_len_size, write_size, &p_data[write_len], &rsp);
        }
        if (ret != NCI_STATUS_OK) {
            NFCLOG_E("t4t write ndef file failed, ret=%d", ret);
            goto cleanup;
        }
        write_len += write_size;
    }

    if (write_len != data_len) {
        NFCLOG_E("t4t write ndef file failed, ndef_len=%d, write_len=%d", data_len, write_len);
        ret = NCI_STATUS_EXT_FAILED;
        goto cleanup;
    }

    if (p_t4t->cc_file.ndef_len_size == T4T_NDEF_FILE_NLEN_SIZE) {
        ndef_len[0] = (data_len >> 8) & 0xFF;
        ndef_len[1] = data_len & 0xFF;
    } else {
        ndef_len[0] = (data_len >> 24) & 0xFF;
        ndef_len[1] = (data_len >> 16) & 0xFF;
        ndef_len[2] = (data_len >> 8) & 0xFF;
        ndef_len[3] = data_len & 0xFF;
    }

    ret = t4t_write_binary(&s_t4t_rw, 0, p_t4t->cc_file.ndef_len_size, ndef_len, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t update ndef len failed, ret=%d", ret);
        goto cleanup;
    }
    p_t4t->ndef_len = data_len;
cleanup:
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS set_tag_read_only(bool is_hard_lock) {
    /**
     * 1. 选择CC文件
     * 2. 更新CC文件中的NDEF_WRITE_ACCESS字段为0xFF
     * 3. 选择NDEF文件
     */
    (void) is_hard_lock;
    tT4T_INFO *p_t4t = &s_t4t_info;
    if (!p_t4t->is_ndef_detected) {
        NFCLOG_E("NDEF not detected");
        return NCI_STATUS_EXT_FAILED;
    }
    if (p_t4t->cc_file.ndef_write_access == T4T_FC_NO_WRITE_ACCESS) {
        NFCLOG_I("NDEF is already read only");
        return NCI_STATUS_OK;
    }
    tNCI_BUF *rsp = NULL;
    tNCI_STATUS ret = t4t_select_file(&s_t4t_rw, T4T_CC_FILE_ID, SELECT_FILE_P2, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select cc file failed, ret=%d", ret);
        goto cleanup;
    }
    p_t4t->cc_file.ndef_write_access = T4T_FC_NO_WRITE_ACCESS;
    ret = t4t_write_binary(&s_t4t_rw, T4T_FC_TLV_OFFSET_IN_CC + T4T_FC_WRITE_ACCESS_OFFSET_IN_TLV, 1, &p_t4t->cc_file.ndef_write_access, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t update ndef write access failed, ret=%d", ret);
        goto cleanup;
    }
    ret = t4t_select_file(&s_t4t_rw, p_t4t->cc_file.ndef_file_id, SELECT_FILE_P2, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("t4t select ndef file failed, ret=%d", ret);
        goto cleanup;
    }
cleanup:
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS on_select() {
    s_t4t_info.is_ndef_aid_selected = false;
    t4t_reset(&s_t4t_rw);
    return NCI_STATUS_OK;
}

#endif // IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)

static tNCI_STATUS on_connect(tNCI_RW_TAG *p_tag) {
    osi_memset_s(&s_t4t_info, sizeof(tT4T_INFO), 0, sizeof(tT4T_INFO));
    tNCI_RF_TECH_PARAM_PA *p_pa = &p_tag->tech_params[p_tag->index].pa;
    if (p_pa->sens_res[0] == 0x44 && p_pa->sens_res[1] == 0x03 && p_pa->sel_res == 0x20) {
        s_t4t_info.is_desfire = true;
    }
    t4t_reset(&s_t4t_rw);
    return NCI_STATUS_OK;
}

static tNCI_STATUS on_disconnect() {
    osi_memset_s(&s_t4t_info, sizeof(tT4T_INFO), 0, sizeof(tT4T_INFO));
    return NCI_STATUS_OK;
}

static tNCI_STATUS presence_check() {
    return rf_t4t_nak_presence();
}

static tNCI_STATUS on_deselect(tNCI_RW_TAG *p_tag) {
    if (!p_tag) {
        NFCLOG_E("tag is null");
        return NCI_STATUS_EXT_INVALID_PARAM;
    }
    if (p_tag->interfaces[p_tag->index] != NCI_RF_INTERFACE_FRAME) {
        // 只有在Interface是Frame是才发送HALT命令
        return NCI_STATUS_OK;
    }
    tNCI_BUF *cmd = alloc_nci_buf_with_cap(0, 1);
    if (cmd == NULL) {
        NFCLOG_E("alloc nci buf failed");
        return NCI_STATUS_EXT_ALLOC_FAILED;
    }
    UINT8_TO_BUF(cmd, T4T_DESELECT_REQ);
    tNCI_STATUS ret = rf_transmit_async(cmd);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("rf transmit async failed, ret=%d", ret);
    }
    free_nci_buf(cmd);
    return ret;
}

static bool t4t_disconnect_when_transmit_fail(tNCI_BUF *p_cmd, tNCI_STATUS status) {
    (void) p_cmd;
    if (status == NCI_STATUS_RF_PROTOCOL_EXCEPTION) {
        if (g_dta_mode_enabled) {
            // 只在DTA模式下断开连接
            return true;
        }
    }
    return false;
}

tNCI_RW_TAG_API g_rw_t4t_api = {
    .on_connect = on_connect,
    .on_disconnect = on_disconnect,
    .presence_check = presence_check,
    .on_deselect = on_deselect,
    .disconnect_when_transmit_fail = t4t_disconnect_when_transmit_fail,
    .max_retry_count = 0, // 不重试
#if IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)
    .on_select = on_select,
    .ndef_detect = ndef_detect,
    .ndef_read = ndef_read,
    .ndef_write = ndef_write,
    .ndef_format = ndef_format,
    .is_ndef_formatable = is_ndef_formatable,
    .set_tag_read_only = set_tag_read_only,
#endif // IS_FEATURE_ON(NCI_FEATURE_RW_NDEF)
};

#endif // IS_FEATURE_ON(NCI_FEATURE_RW_T4T)

